<?php

/**
 * Product and related classes
 *
 * Shopalong, the open source shopping system
 * http://shopalong.blogspot.com/
 *
 * @author Stuart Metcalfe <shoopdev@gmail.com>
 * @copyright Copyright &copy; 2006, Shopalong
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 */

/**
 * The product class
 */

class Product
{
	var $_exists = false;
	var $_id = -1;
	var $_name = '';
	var $_description = '';
	var $_model = '';
	var $_quantity = 0;
	var $_image = '';
	var $_url = '';
	var $_price = 0;
	var $_taxClassId = -1;
	var $_dateAdded = '';
	var $_dateAvailable = '';
	var $_manufacturersId = -1;
	var $_options = array();
	
	// SQL statements
	var $_productCheckSql = 'select count(*) as total from %s p, %s pd where p.products_status = "1" and p.products_id = "%s" and pd.products_id = p.products_id and pd.language_id = "%s"';
	var $_productInfoSql = 'select p.products_id, pd.products_name, pd.products_description, p.products_model, p.products_quantity, p.products_image, pd.products_url, p.products_price, p.products_tax_class_id, p.products_date_added, p.products_date_available, p.manufacturers_id from %s p, %s pd where p.products_status = "1" and p.products_id = "%s" and pd.products_id = p.products_id and pd.language_id = "%s"';
	var $_specialsPriceSql = 'select specials_new_products_price from %s where products_id = "%s" and status';
	var $_incrementViewsSql = 'update %s set products_viewed = products_viewed+1 where products_id = "%s" and language_id = "%s"';
	var $_checkOptionsSql = 'select count(*) as total from %s popt, %s patrib where patrib.products_id="%s" and patrib.options_id = popt.products_options_id and popt.language_id = "%s"';
	var $_optionGroupsSql = 'select distinct popt.products_options_id, popt.products_options_name from %s popt, %s patrib where patrib.products_id="%s" and patrib.options_id = popt.products_options_id and popt.language_id = "%s" order by popt.products_options_name';
	var $_optionsSql = 'select pov.products_options_values_id, pov.products_options_values_name, pa.options_values_price, pa.price_prefix from %s pa, %s pov where pa.products_id = "%s" and pa.options_id = "%s" and pa.options_values_id = pov.products_options_values_id and pov.language_id = "%s"';
	
	/**
	 * Constructor function
	 *
	 * @access public
	 * @param integer|array $init The initialising id or array of values
	 */
	
	function Product($init = null)
	{
		global $lng;
		
		if (is_numeric($init))
		{
			$product_check_query = tep_db_query(sprintf($this->_productCheckSql, TABLE_PRODUCTS, TABLE_PRODUCTS_DESCRIPTION, (int)$init, (int)$lng->getLanguageId()));
			$product_check = tep_db_fetch_array($product_check_query);
			
			if ($product_check['total'] < 1)
			{
				return;
			}
			
			$this->_exists = true;
			$this->_id = $init;
			
			$product_info_query = tep_db_query(sprintf($this->_productInfoSql, TABLE_PRODUCTS, TABLE_PRODUCTS_DESCRIPTION, (int)$this->_id, (int)$lng->getLanguageId()));
			$init = tep_db_fetch_array($product_info_query);
		}
		
		if (is_array($init))
		{
			if (!$this->_exists)
			{
				$product_check_query = tep_db_query(sprintf($this->_productCheckSql, TABLE_PRODUCTS, TABLE_PRODUCTS_DESCRIPTION, (int)$init, (int)$lng->getLanguageId()));
				$product_check = tep_db_fetch_array($product_check_query);
				
				if ($product_check['total'] < 1)
				{
					return;
				}
				
				$this->_exists = true;
			}
			
			$this->_id = $init['products_id'];
			$this->_name = $init['products_name'];
			$this->_description = $init['products_description'];
			$this->_model = $init['products_model'];
			$this->_quantity = $init['products_quantity'];
			$this->_image = $init['products_image'];
			$this->_url = $init['products_url'];
			$this->_price = $init['products_price'];
			$this->_taxClassId = $init['products_tax_class_id'];
			$this->_dateAdded = $init['products_date_added'];
			$this->_dateAvailable = $init['products_date_available'];
			$this->_manufacturersId = $init['manufacturers_id'];
		}
		
		$this->_loadOptions();
	}
	
	/**
	 * Can the product be purchased
	 *
	 * @todo work through the conditions which will indicate whether this product can be purchased.
	 *
	 * @access public
	 * @return boolean Can the product be purchased
	 */
	
	function canBePurchased()
	{
		return true;
	}
	
	/**
	 * Does the product exist
	 *
	 * @access public
	 * @return boolean Does the product exist
	 */
	
	function exists()
	{
		return $this->_exists;
	}
	
	/**
	 * Get the product's date added
	 *
	 * @access public
	 * @param string $format The format using php date() format (default empty)
	 * @return string The product's date added
	 */
	
	function getDateAdded($format = '')
	{
		$date = strtotime($this->_dateAdded);
		return (empty($format)) ? date($format, $date) : $date;
	}
	
	/**
	 * Get the product's date available
	 *
	 * @access public
	 * @param string $format The format using php date() format (default empty)
	 * @return string The product's date available
	 */
	
	function getDateAvailable($format = '')
	{
		$date = strtotime($this->_dateAvailable);
		return (strlen($format) > 0) ? date($format, $date) : $date;
	}
	
	/**
	 * Get the product's description
	 *
	 * @access public
	 * @return string The product's description
	 */
	
	function getDescription()
	{
		return $this->_description;
	}
	
	/**
	 * Get the product's id
	 *
	 * @access public
	 * @return integer The product's id
	 */
	
	function getId()
	{
		return $this->_id;
	}
	
	/**
	 * Get the absolute image url
	 *
	 * @access public
	 * @return string The product's image url
	 */
	
	function getImageSrc()
	{
		return DIR_WS_PRODUCT_IMAGES.$this->_image;
	}
	
	/**
	 * Get the product's manufacturer id
	 *
	 * @access public
	 * @return integer The product's manufacturer id
	 */
	
	function getManufacturersId()
	{
		return $this->_manufacturersId;
	}
	
	/**
	 * Get the product's model
	 *
	 * @access public
	 * @return string The product's model
	 */
	
	function getModel()
	{
		return $this->_model;
	}
	
	/**
	 * Get the product's name
	 *
	 * @access public
	 * @return string The product's name
	 */
	
	function getName()
	{
		return $this->_name;
	}
	
	/**
	 * Get the product's option groups
	 *
	 * @access public
	 * @return array Array of ProductOptionGroup objects 
	 */
	
	function getOptionGroups()
	{
		return $this->_options;
	}
	
	/**
	 * Get the product's price
	 *
	 * @access public
	 * @param string $specialFormat The format string (sprintf format)
	 * @return string The product's price
	 */
	
	function getPrice($specialFormat = '<s>%s</s> <span class="productSpecialPrice">%s</span>')
	{
		global $currencies;
		
		$newPrice = $this->getSpecialPrice();
		
		if ($newPrice > 0)
		{
			$taxRate = tep_get_tax_rate($this->_taxClassId);
			$oldPrice = $currencies->displayPrice($this->_price, $taxRate);
			$newPrice = $currencies->displayPrice($newPrice, $taxRate);
			return sprintf($specialFormat, $oldPrice, $newPrice);
		}
		
		else
		{
			$taxRate = tep_get_tax_rate($this->_taxClassId);
			$price = $currencies->displayPrice($this->_price, $taxRate);
			return $price;
		}
	}
	
	/**
	 * Get the product's stock quantity
	 *
	 * @access public
	 * @return integer The product's stock quantity
	 */
	
	function getQuantity()
	{
		return $this->_quantity;
	}
	
	/**
	 * Get the product's special price
	 *
	 * @access public
	 * @return string The product's special price
	 */
	
	function getSpecialPrice()
	{
		$product_query = tep_db_query(sprintf($this->_specialsPriceSql, TABLE_SPECIALS, (int)$this->_id));
		$product = tep_db_fetch_array($product_query);
		return $product['specials_new_products_price'];
	}
	
	/**
	 * Get the product's tax class id
	 *
	 * @access public
	 * @return integer The product's tax class id
	 */
	
	function getTaxClassId()
	{
		return $this->_taxClassId;
	}
	
	/**
	 * Get the product's url
	 *
	 * @access public
	 * @return string The product's url
	 */
	
	function getUrl()
	{
		return $this->_url;
	}
	
	/**
	 * Does the product have an image
	 *
	 * @access public
	 * @return boolean Does the product have an image
	 */
	
	function hasImage()
	{
		return tep_not_null($this->_image);
	}
	
	/**
	 * Does the product have options/attributes?
	 *
	 * @access public
	 * @return boolean Does the product have options/attributes?
	 */
	
	function hasOptions()
	{
		return sizeof($this->_options) > 0;
	}
	
	/**
	 * Increment the number of times the product has been viewed
	 *
	 * @access public
	 */
	
	function incrementViews()
	{
		global $lng;
		
		if ($this->_exists)
		{
			tep_db_query(sprintf($this->_incrementViewsSql, TABLE_PRODUCTS_DESCRIPTION, (int)$this->_id, (int)$lng->getLanguageId()));
		}
	}
	
	/**
	 * Load attributes for the product
	 *
	 * @todo More work is need on options (previously attributes - please
	 * complete migration to new name).  We need to create a ProductOption
	 * object to hold all this data.
	 *
	 * @access private
	 */
	
	function _loadOptions()
	{
		global $currencies, $lng, $cart;
		
		if ($this->_exists)
		{
			$products_attributes_query = tep_db_query(sprintf($this->_checkOptionsSql, TABLE_PRODUCTS_OPTIONS, TABLE_PRODUCTS_ATTRIBUTES, (int)$this->_id, (int)$lng->getLanguageId()));
			$products_attributes = tep_db_fetch_array($products_attributes_query);
			
			if ($products_attributes['total'] > 0) {
				$products_options_name_query = tep_db_query(sprintf($this->_optionGroupsSql, TABLE_PRODUCTS_OPTIONS, TABLE_PRODUCTS_ATTRIBUTES, (int)$this->_id, (int)$lng->getLanguageId()));
				
				while ($products_options_name = tep_db_fetch_array($products_options_name_query)) {
					$options = array();
					$products_options_array = array();
					$products_options_query = tep_db_query(sprintf($this->_optionsSql, TABLE_PRODUCTS_ATTRIBUTES, TABLE_PRODUCTS_OPTIONS_VALUES, (int)$this->_id, (int)$products_options_name['products_options_id'], (int)$lng->getLanguageId()));
					
					while ($products_options = tep_db_fetch_array($products_options_query)) {
						$id = $products_options['products_options_values_id'];
						$name = $products_options['products_options_values_name'];
						$value = $products_options['options_values_price'];
						$options[] = new ProductOption($id, $name, $value, $this);
					}
					
					if (isset($cart->contents[$this->_id]['attributes'][$products_options_name['products_options_id']])) {
						$selected_attribute = $cart->contents[$this->_id]['attributes'][$products_options_name['products_options_id']];
					} else {
						$selected_attribute = false;
					}
					
					$id = $products_options_name['products_options_id'];
					$name = $products_options_name['products_options_name'];
					$this->_options[] = new ProductOptionGroup($id, $name, $options);
				}
			}
		}
	}
}

/**
 * The product option group class
 */

class ProductOptionGroup
{
	var $_id;
	var $_name;
	var $_options;
	
	/**
	 * Constructor function
	 *
	 * @access public
	 * @param integer $id
	 * @param string $name
	 * @param array $options Array of ProductOption objects
	 */
	
	function ProductOptionGroup($id, $name, $options)
	{
		$this->_id = $id;
		$this->_name = $name;
		$this->_options = $options;
	}
	
	/**
	 * Get the option group id
	 *
	 * @access public
	 * @return integer
	 */
	
	function getId()
	{
		return $this->_id;
	}
	
	/**
	 * Get the option group name
	 *
	 * @access public
	 * @return string
	 */
	
	function getName()
	{
		return $this->_name;
	}
	
	/**
	 * Get the options for this group
	 *
	 * @access public
	 * @return array
	 */
	
	function getOptions()
	{
		return $this->_options;
	}
}

/**
 * The product option class
 */

class ProductOption
{
	var $_id;
	var $_name;
	var $_value;
	var $_product;
	
	/**
	 * Constructor function
	 *
	 * @access public
	 * @param integer $id
	 * @param string $string
	 * @param float $value
	 * @param object $product Product object
	 */
	
	function ProductOption($id, $name, $value, &$product)
	{
		$this->_id = $id;
		$this->_name = $name;
		$this->_value = $value;
		$this->_product = $product;
	}
	
	/**
	 * Get the option id
	 *
	 * @access public
	 * @return integer
	 */
	
	function getId()
	{
		return $this->_id;
	}
	
	/**
	 * Get the option group name
	 *
	 * @access public
	 * @return string
	 */
	
	function getName()
	{
		return $this->_name;
	}
	
	/**
	 * Get the option value (above or below the product price)
	 *
	 * @access public
	 * @return float Can be "+" or "-"
	 */
	
	function getValue()
	{
		return $this->_value;
	}
	
	/**
	 * Get the option value formatted for display
	 *
	 * @access public
	 * @return string
	 */
	
	function getValueFormatted()
	{
		global $currencies;
		
		$price = $currencies->displayPrice(
			$this->_value,
			tep_get_tax_rate($this->_product->getTaxClassId())
		);
		
		return $price;
	}
}

?>
